# http://www.cnblogs.com/studyzy/p/4316118.html

Data Frame一般被翻译为数据框，感觉就像是R中的表，由行和列组成，与Matrix不同的是，每个列可以是不同的数据类型，而Matrix是必须相同的。
Data Frame每一列有列名，每一行也可以指定行名。如果不指定行名，那么就是从1开始自增的Sequence来标识每一行。

# 参考
# http://www.cnblogs.com/studyzy/p/4316118.html
# https://cos.name/cn/topic/12365/


1. 创建数据框
(1)使用已有向量建立数据框
patientID <- c(1:4)
age <- c(25,31,42,57)
diabetes <- c("Type1","Type2","Type3","Type4")
status <- c("Poor","Improved","Excellent","Poor")
patientdata <- data.frame(patientID,age,'diabetesType'=diabetes,status) #可以修改列名
patientdata
结果
patientID age diabetesType status
1 1 25 Type1 Poor
2 2 31 Type2 Improved
3 3 42 Type3 Excellent
4 4 57 Type4 Poor

(2)使用向量直接建立数据框
student<-data.frame(ID=c(11,12,13),Name=c("Devin","Edward","Wenli"),
  Gender=c("M","M","F"),Birthdate=c("1984-12-29","1983-5-6","1986-8-8"))
student
输出
ID   Name Gender  Birthdate
1 11  Devin      M 1984-12-29
2 12 Edward      M   1983-5-6
3 13  Wenli      F   1986-8-8

(3)另外也可以使用read.table() read.csv()读取一个文本文件，返回的也是一个Data Frame对象。
读取数据库也是返回Data Frame对象。

比如：
text1=read.table('001.txt') #其中文本文件001.txt最后一行是空行，否则会报错
text2=read.table('001.txt',header=F)  #如果没有标题，用header设置
text3=read.table('001.txt',header=F,sep=",") #如果是逗号分隔的，则用sep设置（默认是tab分隔的）

(4)如果是从excel、txt中复制到内存的，可以直接读取内存。
text4=read.table('clipboard',header=T) #设置有标题



2.行名列名
使用names函数可以查看列名，如果要查看行名，需要用到row.names函数。这里我们希望将ID作为行名，那么可以这样写：
row.names(student)<-student$ID
更简单的办法是在初始化date.frame的时候，有参数row.names可以设置行名的向量。





3. 获取元素
与Matrix一样，使用[行Index,列Index]的格式可以访问具体的元素。
student

(1)访问某行，从1计数。
student[1,]

(2)访问某列，从1计数。可以使用[[或者$来访问，返回的是Vector类型的。
c2=student[,2]
class(c2) #"factor"

student[[2]] #或者这样
student$Name #使用列名，结果一样。
student[["Name"]]


(3)返回【子列】数据框
v1=student[2]
class(v1) # "data.frame"
student[1:3] #取前3列
student[c('Name','Birthdate')] #如果取不连续的列，需要用列名
(4)返回【子行】数据框
student[1:3,] #1到3行
student[c(1,3),] #1和3行

(5)
使用attach和detach函数可以使得访问列时不需要总是跟着变量名在前面。
比如要打印所有Name，那么可以写成：
rownames(student)=student$Name
student #用名字命名每行。
输出
  ID   Name Gender  Birthdate
  Devin  11  Devin      M 1984-12-29
  Edward 12 Edward      M   1983-5-6
  Wenli  13  Wenli      F   1986-8-8

student['Devin',]#输出Devin行 Devin 11 Devin      M 1984-12-29
  
attach(student)
print(Name) #也就是只能识别列名
print(Devin) #报错
detach(student)

(6)用with函数写法上更简洁：
with(student,{print(Gender)})

#使用局部变量
with(student,{
  n<-Birthdate
  print(n)
})
n #Error: object 'n' not found

这里的n作用域只在大括号内，
如果想在with函数中对全局的变量进行赋值，那么需要使用<<-这样一个运算符。


(7)查询与子集-使用which对行过滤
查询一个Date Frame，返回一个满足条件的子集，这相当于数据库中的表查询，是非常常见的操作。
使用行和列的Index来获取子集是最简单的方法，前面已经提到过。
如果我们使用布尔向量，配合which函数，可以实现对行的过滤。
比如我们要查询所有Gender为F的数据，那么我们首先对student$Gender==“F”，得到一个布尔向量:FALSE FALSE  TRUE,
然后使用which函数可以将布尔向量中TRUE的Index返回，所以我们的完整查询语句就是：

boys=student[which(student$Gender=="M"),]
boys


注意这里列Index并没有输入，如果我们只想知道所有女生的年龄，那么可以改为：
student[which(student$Gender=="M"),"Age"]
student[which(student$Gender=="M"),c("Age","Age3")]

这样的查询写法还是复杂了点，可以直接使用subset函数，那么查询会简单些，比如我们把查询条件改为年龄<30的女性，查姓名和年龄，那么查询语句为：
subset(student,Gender=="F" & Age>20 ,select=c("Name","Age"))


(8)使用SQL查询Data Frame
对于使用了多年SQL的人来说，如果能够直接写SQL语句对Data Frame进行查询操作，那真是太酸爽了，
结果还真有这么一个包：sqldf。
同样是前面的需求，对应的语句就是：
library(sqldf)
result<-sqldf("select Name,Age from student where Gender='F' and Age>20")
result

result<-sqldf("select Name,Age from student where Gender='M' and Age>20")
result
result[1,][1]



(9)遍历数据框
常规解决不了的就用遍历吧。

x=dim(student)[1]
y=dim(student)[2]
#遍历每个元素
for(i in 1:x){
  for(j in 1:y){
    print(student[i,j])
  }
}

#对每行进行遍历，找出4-9月出生的学生
student
tmp=c()
for(i in 1:x){
  birthday=student[i,4]
  month=as.integer(substr(birthday,6,7))
  if(month>=4 && month<=9){
    tmp=c(tmp,i)
  }
}
tmp
print(student[tmp,])

#还有更好的方法吗？使用apply家族函数，能避免循环。
#http://www.cnblogs.com/cloudtj/articles/5523811.html
#apply(X, MARGIN, FUN, ...) MARGIN: 按行计算或按按列计算，1表示按行，2表示按列
rs=apply(student,1,function(x){
  #print("结果")
  birthday=x["Birthdate"]
  month=as.integer(substr(birthday,6,7))
  if(month>=4 && month<=9){
    return(x[["Name"]])
  }
})
rs2=unlist(rs)
rs3=as.character(rs2)
rs3
student[rs3,]
















4.修改

(1)查看和修改数据类型
str(student)
输出
  'data.frame':	3 obs. of  4 variables:
    $ ID       : num  11 12 13
  $ Name     : Factor w/ 3 levels "Devin","Edward",..: 1 2 3
  $ Gender   : Factor w/ 2 levels "F","M": 2 2 1
  $ Birthdate: Factor w/ 3 levels "1983-5-6","1984-12-29",..: 2 1 3

默认情况下，字符串向量都会被自动识别成Factor，也就是说，ID是数字类型，其他的3个列都被定义为Factor类型了。
显然这里Name应该是字符串类型，Birthdate应该是Date类型，我们需要对列的数据类型进行更改：
student$Name<-as.character(student$Name)
student$Birthdate<-as.character(student$Birthdate)
str(student) #Birthdate已经是字符型了 $ Birthdate: chr  "1984-12-29" "1983-5-6" "1986-8-8"

一般修改factor到日期或数字，需要先转换为字符串
student$Birthdate<-as.Date(student$Birthdate)

更改数据框所有数据的格式
df<- lapply(df,as.numeric)



(2) 添加新列
对于以及存在的student对象，我们希望增加Age列，该列是根据Birthdate算出来的。
首先需要知道怎么算年龄。我们可以使用日期函数Sys.Date()获得当前的日期，
然后使用format函数获得年份，然后用两个年份相减就是年龄。
好像R并没有提供几个能用的日期函数，我们只能使用format函数取出年份部分，然后转换为int类型相减。

#写法1【不好】
#时间日期总结
#http://blog.csdn.net/xiangyong58/article/details/50458532
student$Age=as.Date(Sys.Date())-student$Birthdate
str(student)
student$Age=as.integer(student$Age/365) #这处理的不好，应该获取年，减去当前年
str(student)

#写法2
student$Age2<-as.integer(format(Sys.Date(),"%Y")) - as.integer(format(student$Birthdate,"%Y"))
str(student)

这样写似乎太长了，我们可以用within函数，这个函数和之前提到过的with函数类似，可以省略变量名，
不同的地方是within函数可以在其中修改变量，也就是我们这里增加Age列：

#写法3
student<-within(student,{
  Age3=as.integer(format(Sys.Date(),"%Y"))-as.integer(format(Birthdate,"%Y"))
})
str(student)


(3)删除行
student2=student[-1,] #减去第1行
student2


(4)增加列
student$class=c("art","math","law")
str(student)


(5)删除列
student2 <- subset(student, select = -Age )
student2 #已经删除了不靠谱的age列

student3=student2[,-7] #减去第7列

colNum=dim(student2)[2]
student4=student2[,-colNum] #减去最后一列
student4






5.连接数据框
(1)使用merge函数横向连接
对于数据库来说，对多表进行join查询是一个很正常的事情，
那么在R中也可以对多个Data Frame进行连接，这就需要使用merge函数。
比如除了前面申明的student对象外，我们再申明一个score变量，记录了每个学生的科目和成绩：
score<-data.frame(SID=c(11,11,12,12,13),Course=c("Math","English","Math","Chinese","Math"),Score=c(90,80,80,95,96))
score
输出
  SID  Course Score
  1  11    Math    90
  2  11 English    80
  3  12    Math    80
  4  12 Chinese    95
  5  13    Math    96
这里的SID就是Student里面的ID，相当于一个外键，现在要用这个ID进行inner join操作，那么对应的R语句就是：
result<-merge(student,score,by.x="ID",by.y="SID")
result

正如我们期望的一样join在了一起。


(2)使用cbind横向连接
student
score
rs=cbind(student,score[1:3,])   ##按照行连接，行数必须相同
rs #没有外键关联，直接连接。



(3)使用rbind纵向连接
除了join，另外一个操作就是union，这也是数据库常用操作，
那么在R中如何将两个列一样的Data Frame Union联接在一起呢？
虽然R语言中有union函数，但是不是SQL的Union的意思，我们要实现Union功能，需要用到rbind函数。

rbind的两个Data Frame必须有相同的列，比如我们再申明一个student2，将两个变量rbind起来：
student2<-data.frame(ID=c(21,22),Name=c("Yan","Peng"),Gender=c("F","M"),Birthdate=c("1982-2-9","1983-1-16"),Age=c(32,31))
student3<-data.frame(ID=c(11,12,13),Name=c("Devin","Edward","Wenli"),Gender=c("M","M","F"),Birthdate=c("1984-12-29","1983-5-6","1986-8-8"),Age=c(32,31,30))
student2
student3
studentAll=rbind(student2,student3) ##按照列连接，列数必须相同
studentAll




6.数据框的计算，按某列求平均值
student<-data.frame(ID=c(11,12,13),Name=c("Devin","Edward","Wenli"),Gender=c("M","M","F"),Birthdate=c("1984-12-29","1983-5-6","1986-8-8"))
score<-data.frame(SID=c(11,11,12,12,13,13),Course=c("Math","English","Math","Chinese","Math","Chinese"),Score=c(90,80,80,95,96,85))
student
score
result<-merge(student,score,by.x="ID",by.y="SID")
result

(1)求各科目平均分
courseMean=data.frame(course=c(),avg=c(),sum=c(),count=c())
for(i in unique(score$Course)){
  subgrp=score[which(score$Course==i),][['Score']] #分组
  #求行的平均数
  count=length(subgrp);#计数
  sum=sum(subgrp) #总分
  avg=mean(subgrp) #平均分
  #合并到科目平均数df
  courseMean=rbind(courseMean,data.frame(course=c(i),avg=c(avg),sum=c(sum),count=c(count)))
}
courseMean


(2)求每个人的平均分。修改分组，其他不变化
stuMean=data.frame(course=c(),avg=c(),sum=c(),count=c())
for(i in unique(result$Name)){ #循环(modify)
  subgrp=score[which(result$Name==i),][['Score']] #分组(modify)
  #求行的平均数
  count=length(subgrp);#计数
  sum=sum(subgrp) #总分
  avg=mean(subgrp) #平均分
  #合并到科目平均数df
  stuMean=rbind(stuMean,data.frame(course=c(i),avg=c(avg),sum=c(sum),count=c(count)))
}
stuMean


(3)使用tapply函数
# http://bbs.pinggu.org/thread-3934132-1-1.html
# tapply(X, INDEX, FUN = NULL, ..., simplify = TRUE)
result
tapply(result[,6],result[,5],mean)
输出
  Chinese  English     Math 
  90.00000 80.00000 88.66667


tapply(result[,6],result[,c(2,5)],sum) #计数
tapply(result[,6],result[,c(2,5)],sum) #两个维度的数据透视表
输出
         Course
  Name     Chinese English Math
  Devin       NA      80   90
  Edward      95      NA   80
  Wenli       85      NA   96


(4)数据透视表
# http://bbs.pinggu.org/thread-3797604-1-1.html
dat1=data.frame(host=c("A","A","A","E","E","G"),
                guest=c("C","B","D","Q","F","W"),
                num=c(2,1,2,3,0,2))
tapply(dat1[,3],dat1[,c(1,2)],mean)

#或者使用
library(reshape)
dat2=cast(dat1,host~guest,value="num")
dat2


这个colMeans函数能用吗？
